﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace HexDemo
{
    public class ImmediateMode
    {
        private GraphicsDevice GraphicsDevice;
        public BasicEffect Effect;

        VertexPositionColorTexture[] VertexBuffer = new VertexPositionColorTexture[512];
        int VertexCount = 0;
        PrimitiveType primType = PrimitiveType.TriangleStrip;
        bool Drawing = false;

        public ImmediateMode(GraphicsDevice GraphicsDevice)
        {
            reset(GraphicsDevice);
						
        }

        public void reset(GraphicsDevice GraphicsDevice)
        {
            this.GraphicsDevice = GraphicsDevice;
            Effect = new BasicEffect(GraphicsDevice);
            _matrixCount = 1;
            MatrixStack[0] = Matrix.Identity;

            Effect.VertexColorEnabled = true;
            Effect.TextureEnabled = true;

        }

        public void SetCamera(OrthographicCamera Camera)
        {
            Effect.Projection = Camera.Projection;
            Effect.View = Camera.View;
        }

        private Matrix[] MatrixStack = new Matrix[32];
        int _matrixCount = 0;

        public void PushMatrix(Matrix M)
        {
            Matrix _m = M * TopMatrix;
            MatrixStack[_matrixCount] = _m;
            _matrixCount += 1;
        }

        public void PushCleanMatrix(Matrix M)
        {
            MatrixStack[_matrixCount] = M;
            _matrixCount += 1;
        }

        public void PopMatrix()
        {
            if (_matrixCount <= 1) throw new InvalidOperationException();
            _matrixCount -= 1;
        }

        public Matrix TopMatrix { get { return MatrixStack[_matrixCount - 1]; } }

        public void BindTexture(Texture2D Texture)
        {
            Effect.Texture = Texture;
        }

        public void Prepare()
        {
            Effect.World = TopMatrix;
            Effect.CurrentTechnique.Passes[0].Apply();
        }

        public void Begin(PrimitiveType primType)
        {
            if (Drawing) throw new InvalidProgramException("Must call End before calling Begin again.");
            Drawing = true;
            this.primType = primType;
            VertexCount = 0;
            Effect.World = TopMatrix;
        }

        public void Vertex(Vector3 Position, Color Color, Vector2 Texture)
        {
            if (VertexCount >= 512) Flush();
            VertexBuffer[VertexCount].Position = Position;
            VertexBuffer[VertexCount].Color = Color;
            VertexBuffer[VertexCount].TextureCoordinate = Texture;
            ++VertexCount;
        }

        public void End()
        {
            Flush();
            Drawing = false;
            VertexCount = 0;
        }

        private void Flush()
        {
            Effect.CurrentTechnique.Passes[0].Apply();
            int PrimitiveCount = 0;
            int Remainder = 0;

            switch (primType)
            {
                case PrimitiveType.TriangleList:
                    PrimitiveCount = (int)(VertexCount / 3);
                    Remainder = VertexCount % 3;
                    break;
                case PrimitiveType.TriangleStrip:
                    PrimitiveCount = VertexCount - 2;
                    Remainder = 2;
                    break;
                default:
                    break;
            }

            if (PrimitiveCount <= 0) return;

            GraphicsDevice.DrawUserPrimitives<VertexPositionColorTexture>(primType, VertexBuffer, 0, PrimitiveCount);

            for (int i = 0; i < Remainder; ++i)
                VertexBuffer[i] = VertexBuffer[VertexCount - Remainder + i];
            VertexCount = Remainder;
        }
    }
}
